<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="utf-8">
	<title>仿重力小球</title>
	<style type="text/css">
		body {
			padding:0;
			margin:0;;
			overflow: hidden;
		}
		#myCanvas {
			display: block;
		}
	</style>
	<script type="text/javascript">
		onload = function(){
			canvas = document.getElementById('myCanvas');
			cas = canvas.getContext("2d");
			canvas.width = window.innerWidth;
			canvas.height = window.innerHeight;
			ballRadius = 10, g = 9.8, mocali = 0.5, balls = [], collarg = 0.8;
			pxpm = canvas.width/20;

			for(var i = 0;i < 100; i ++){
				var ball = new Ball(getRandom(ballRadius,canvas.width-ballRadius),
							getRandom(ballRadius,canvas.height-ballRadius),ballRadius, 
							"rgba("+parseInt(Math.random()*255)+","+parseInt(Math.random()*255)+","+parseInt(Math.random()*255)+",1)");
				balls.push(ball);
			}

			var t0 = new Date();
			animate()

			canvas.onclick = function(event){
				event = event || window.event;
				var x = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft - canvas.offsetLeft;
				var y = event.clientY + document.body.scrollTop + document.documentElement.scrollTop - canvas.offsetTop;

				balls.forEach(function(){
					this.vx = (x-this.x)/40;
					this.vy = (y-this.y)/40;
				});
			}

			function getRandom(a,b){
				return Math.random()*(b-a)+a;
			}

			function animate(){
				cas.clearRect(0,0,canvas.width,canvas.height);
				cas.save();
				cas.fillStyle = "rgba(255,255,255,0.2)";
				cas.fillRect(0,0,canvas.width,canvas.height);
				cas.restore();

				var t = 16/1000;
				collision();
				balls.forEach(function(){
					this.run(t);
				});

				if("requestAnimationFrame" in window){
					requestAnimationFrame(animate);
				}else if("webkitRequestAnimationFrame" in window){
					webkitRequestAnimationFrame(animate);
				}else if("msRequestAnimationFrame" in window){
					msRequestAnimationFrame(animate);
				}else if("mozRequestAnimationFrame" in window){
					mozRequestAnimationFrame(animate);
				}
			}
		}

		function collision(){
			for(var i=0;i<balls.length;i++){
				for(var j=0;j<balls.length;j++){
					var b1 = balls[i],b2 = balls[j];
					if(b1 !== b2){
						var rc = Math.sqrt(Math.pow(b1.x-b2.x,2) + Math.pow(b1.y-b2.y,2));
						if(Math.ceil(rc)<(b1.radius + b2.radius)){
							var ax = ((b1.vx - b2.vx)*Math.pow((b1.x - b2.x), 2) + (b1.vy - b2.vy)*(b1.x - b2.x)*(b1.y - b2.y))/Math.pow(rc,2);
							var ay = ((b1.vy - b2.vy)*Math.pow((b1.y - b2.y), 2) + (b1.vx - b2.vx)*(b1.x - b2.x)+(b1.y - b2.y))/Math.pow(rc,2);

							b1.vx = (b1.vx - ax)*collarg;
							b1.vy = (b1.vy - ay)*collarg;
							b2.vx = (b2.vx + ax)*collarg;
							b2.vy = (b2.vy + ay)*collarg;

							var clength = ((b1.radius + b2.radius) - rc)/2;
							var cx = clength*(b1.x - b2.x)/rc;
							var cy = clength*(b1.y - b2.y)/rc;
							b1.x = b1.x + cx;
							b1.y = b1.y + cy;
							b2.x = b2.x - cx;
							b2.y = b2.y - cy;
						}
					}
				}
			}
		}
		Array.prototype.forEach = function(callback){
			for(var i = 0; i < this.length; i ++){
				callback.call(this[i]);
			}
		}
		var Ball = function(x,y,r,color){
			this.x = x;
			this.y = y;
			this.oldx = x;
			this.oldy = y;
			this.vx = 0;
			this.vy = 0;
			this.radius = r;
			this.color = color;
			this.candrod = true;
		}

		Ball.prototype = {
			paint:function(){
				cas.save();
				cas.beginPath();
				cas.arc(this.x, this.y, this.radius-1, 0 , Math.PI*2);
				cas.fillStyle = this.color;
				cas.fill();
				cas.strokeStyle = "#fff";
				cas.stroke();
				cas.beginPath();
				cas.arc(this.x, this.y, 3 , 0 ,Math.PI*2);
				cas.fillStyle = "rgba(255,255,255,.5)";
				cas.fill();
				cas.restore();
				this.moving = false;
			},
			run:function(t){
				if(!this.candrod){
					this.paint();
					return
				};
				this.oldx = this.x;
				this.oldy = this.y;

				this.vx += this.vx > 0 ? -mocali*t : mocali*t;
				this.vy = this.vy + g*t;

				this.x += t*this.vx*pxpm;
				this.y += t*this.vy*pxpm;

				if(this.y > canvas.height - ballRadius || this.y < ballRadius){
				  this.y = this.y < ballRadius ? ballRadius : (canvas.height - ballRadius);
				  this.vy = -this.vy*collarg;
				}
				if(this.x > canvas.width - ballRadius || this.x < ballRadius){
				  this.x = this.x < ballRadius ? ballRadius : (canvas.width - ballRadius);
				  this.directionX = !this.directionX;
				  this.vx = -this.vx*collarg; 
				}
				this.paint();
			},
		}

	</script>
</head>
<body>
	<div>
		<canvas id="myCanvas" width="1200" height="600" style="background-color:black;"></canvas>
	</div>
</body>
</html>